﻿using Microsoft.EntityFrameworkCore.Storage;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StackExchange.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.Unicode;
using System.Threading;
using System.Threading.Tasks;

namespace 综合同步
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class RedisFormat
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
        public char[] filePath;
        public int width;
        public int height;
        public int framerate;
        public int bitrate;
        public int state; //begin:1,end:2
        public Int64 timestamp;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public byte[] data = new byte[1];
    }
    public class RedisClientBll
    {
        bool m_bStop = false;
        
        #region 路径
        static string filePath;
        static object mutex = new object();
        public static string sPath{
            get{
                lock(mutex) return filePath;
            }
            set{
                lock(mutex) filePath = value;
            }
        }
        #endregion

        #region 实时显示数据
        public static ConcurrentQueue<byte[]> realTimeData = new ConcurrentQueue<byte[]>();
        #endregion

        [DllImport("DHVideo2.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void BeginMuxing(ref IntPtr handle, string fileName,int width,int height,int framerate,int bitrate, int code, bool encode);
        [DllImport("DHVideo2.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void Muxing(ref IntPtr handle, byte[] data, int lens);
        [DllImport("DHVideo2.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void EndMuxing(ref IntPtr handle);
        [DllImport("DHVideo2.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern Int64 GetFileTimeSpan(string file);
         [DllImport("DHVideo2.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void SetLogDisplay(bool bshow);

        public RedisClientBll(string ip,ushort port,string password="")
        {
            if(LocalServer.GetConfigValue<bool>("encodeLog")) SetLogDisplay(true);
            RedisExchange redis_data = new RedisExchange(ip,port,password);
            Task.Run(new Action(() =>
            {
                Dictionary<string, DateTime> fileTime = new Dictionary<string, DateTime>();
                Dictionary<string, IntPtr> fileHandle = new Dictionary<string, IntPtr>();
                Dictionary<string, Int64> fileLen = new Dictionary<string, Int64>();
                while (!m_bStop)
                {
                    try{
                        if(!redis_data.Exists("redis_data")){
                            Thread.Sleep(3);
                            continue;
                        }
                        byte[] data = redis_data.GetData("redis_data");
                        if(data==null) continue;
                        if(WsData.WebSocketBegin) realTimeData.Enqueue(data);//缓存数据
                        if (BitConverter.ToInt32(data, 256+5*sizeof(int)) == 1)
                        {
                            IntPtr handle = IntPtr.Zero;
                            string file = Encoding.Default.GetString(data, 0, 256).TrimEnd('\0');
                            byte[] encode_data = data.Skip(256 + 6 * sizeof(int)+2*sizeof(Int64)).ToArray();
                            if(!fileHandle.TryGetValue(file,out handle))
                            {
                                string filepath = Path.Combine(LocalServer.GetConfigValue<string>("DataPath"),Path.GetDirectoryName(file));
                                if(!Directory.Exists(filepath))
                                    Directory.CreateDirectory(filepath);
                                BeginMuxing(ref handle, Path.Combine(filepath,Path.GetFileName(file)), 
                                    BitConverter.ToInt32(data, 256 + 0 * sizeof(int)),
                                    BitConverter.ToInt32(data, 256 + 1 * sizeof(int)),
                                    BitConverter.ToInt32(data, 256 + 2 * sizeof(int)),
                                    BitConverter.ToInt32(data, 256 + 3 * sizeof(int)),
                                    BitConverter.ToInt32(data, 256 + 4 * sizeof(int)),false);
                                fileHandle.Add(file,handle);
                                fileTime.Add(file,DateTime.Now);
                                fileLen.Add(file,0);
                            }
                            
                            if(fileTime.ContainsKey(file))
                                fileTime[file] = DateTime.Now;
                            if(fileLen.ContainsKey(file))
                                fileLen[file] += encode_data.Length;
                            Muxing(ref handle, encode_data, encode_data.Length);
                          
                            ClearHandle(fileTime,fileHandle,file);
                        }
                        else if (BitConverter.ToInt32(data, 256 + 5 * sizeof(int)) == 2)
                        {
                            IntPtr handle = IntPtr.Zero;
                            string file = Encoding.Default.GetString(data, 0, 256).TrimEnd('\0');
                            if(fileHandle.TryGetValue(file, out handle))
                            {
                                EndMuxing(ref handle);
                                fileHandle.Remove(file);
                            }
                            if(fileLen.ContainsKey(file)){
                                if(BitConverter.ToInt64(data, 256 + 6 * sizeof(int))!=fileLen[file])
                                    FileLogOperator.Error("文件接收错误，长度不一致:"+file);
                                fileLen.Remove(file);
                            }
                                
                            //清理未结尾文件
                            ClearHandle(fileTime,fileHandle,file);
                        }
                    }catch(Exception e){
                        FileLogOperator.Info(e.Message);
                    }
                }
            }));

            RedisExchange redis_table = new RedisExchange(ip,port,password);
            Task.Run(new Action(() =>
            {
                while (!m_bStop)
                {
                    try{
                        if(!redis_data.Exists("redis_table")){
                            Thread.Sleep(3);
                            continue;
                        }
                        string data = Encoding.UTF8.GetString(redis_data.GetData("redis_table"));
                        JObject ja = JsonConvert.DeserializeObject<JObject>(data.Replace("\\n","").Replace("\\\"","\""));
                        switch(ja["Table"].ToString())
                        {
                            case "Arcing":{
                                ja["Data"]["OccurTime"] = new DateTime(1970, 1, 1, 8, 0, 0, 0).AddSeconds(ja["Data"]["OccurTime"].Value<Int64>());
                                TBArcing arcInfo = JsonConvert.DeserializeObject<TBArcing>(ja["Data"].ToString());
                                arcInfo.SensorNum = arcInfo.SensorNum.Insert(0,LocalServer.GetConfigValue<string>("Id")+(string.IsNullOrEmpty(arcInfo.SensorNum)?"":"-"));
                                WRDataBase.instance().ArcingEntities.Add(arcInfo);
                                WRDataBase.instance().SaveChanges();
                                FileLogOperator.Info("燃弧数据保存成功，持续时间"+arcInfo.Span+"ms");
                                break;
                            }
                            case "Temperature":{
                                ja["Data"]["OccurTime"] = new DateTime(1970, 1, 1, 8, 0, 0, 0).AddSeconds(ja["Data"]["OccurTime"].Value<Int64>());
                                TBTemperature tempInfo = JsonConvert.DeserializeObject<TBTemperature>(ja["Data"].ToString());
                                tempInfo.SensorNum = tempInfo.SensorNum.Insert(0,LocalServer.GetConfigValue<string>("Id")+(string.IsNullOrEmpty(tempInfo.SensorNum)?"":"-"));
                                WRDataBase.instance().TemperatureEntities.Add(tempInfo);
                                WRDataBase.instance().SaveChanges();
                                FileLogOperator.Info("温度数据保存成功，"+tempInfo.TempPath);
                                break;
                            }
                            case "VideoInfo":{
                                ja["Data"]["OccurTime"] = new DateTime(1970, 1, 1, 8, 0, 0, 0).AddSeconds(ja["Data"]["OccurTime"].Value<Int64>());
                                TBVideoInfo videoInfo = JsonConvert.DeserializeObject<TBVideoInfo>(ja["Data"].ToString());
                                videoInfo.SensorNum = videoInfo.SensorNum.Insert(0,LocalServer.GetConfigValue<string>("Id")+(string.IsNullOrEmpty(videoInfo.SensorNum)?"":"-"));
                                WRDataBase.instance().VideoInfoEntities.Add(videoInfo);
                                WRDataBase.instance().SaveChanges();
                                FileLogOperator.Info("视频数据保存成功，"+videoInfo.VideoDir);
                                break;
                            }
                            default:break;
                        }
                    }
                    catch(Exception e){
                        FileLogOperator.Info(e.Message);
                    }
                }
            }));

            /////////////////////////////////////////////////////
            TestData(ip,port,password);
        }
        ~RedisClientBll()
        {
            m_bStop = true;
        }
        void ClearHandle(Dictionary<string, DateTime> fileTime,
            Dictionary<string, IntPtr> fileHandle,string file)
        {
            IntPtr handle = IntPtr.Zero;
            fileTime.Where(o=>o.Key!=file&& (DateTime.Now-o.Value).TotalMinutes>1).
                Select(o=>o.Key).ToList().ForEach(o=>{
                    fileTime.Remove(o);
                    if(fileHandle.TryGetValue(o, out handle))
                    {
                        EndMuxing(ref handle);
                        FileLogOperator.Info("文件长时间未接受到数据提前结束:"+o,ConsoleColor.Yellow);
                        fileHandle.Remove(o);
                    }
                });
        }
        void TestData(string ip,ushort port,string password)
        {
            string file = @"D:\DH_Work\鼎汉自研相机\视频压缩\DHVideo2\encode.h264";
            Task.Run(new Action(() =>{
                Int64 alens = 0;
                byte[] buf = new byte[1024];
                RedisExchange redis_test = new RedisExchange(ip,port,password);
                FileStream fs = File.OpenRead(file);
                while(true){
                    Int64 len = fs.Read(buf,0,1024);
                    if(len>0){
                        alens += len;
                        byte[] file1 = Encoding.Default.GetBytes("红外\\test.h264").ToArray();
                        byte[] file2 = Encoding.Default.GetBytes("视频\\test.h264").ToArray();
                        byte[] data = new int[]{1920,1080,60,1920*1080,27,1}.SelectMany(o=>BitConverter.GetBytes(o)).
                            Concat(new Int64[]{alens,0}.SelectMany(o=>BitConverter.GetBytes(o))).
                            Concat(buf.Take((int)len)).ToArray();
                        redis_test.SetData("redis_data",file1.Concat(new byte[256-file1.Length]).Concat(data).ToArray());
                        redis_test.SetData("redis_data",file2.Concat(new byte[256-file1.Length]).Concat(data).ToArray());
                        Thread.Sleep(40);
                    }
                    else{
                        fs.Close();
                        fs = File.OpenRead(file);
                    }
                }
            }));
        }
    }
}
